//
//  tdl/meta/type_list_impl.h
//  Created by tommywang on 13-2-13.
//

#ifndef tdl_meta_typelist_h
#define tdl_meta_typelist_h

#include "meta/btypes.h"
using namespace tdl;

namespace tdl {
////////////////////////
namespace tlop {
///////////////////////////
#define TRAITS_HEAD_TAIL(TLIST) \
			typedef typename TLIST::head head; \
			typedef typename TLIST::tail tail
	
// length of type_list
template <typename Tlist> struct length
{
	TRAITS_HEAD_TAIL(Tlist);
	enum { value = 1 + length<tail>::value };
};
template <> struct length<null_type>
{
	enum { value = 0 };
};

// get type of type_list[index]
// if i < 0 or i >= length<Tlist>, result type is U
template <typename Tlist, int i, typename U = null_type, bool is_negetive = (i < 0 || i >= length<Tlist>::value)> struct at;
template <typename Tlist, int i, typename U> struct at<Tlist, i, U, true>
{
	typedef U type;
};
template <typename Tlist, int i, typename U> struct at<Tlist, i, U, false>
{
	TRAITS_HEAD_TAIL(Tlist);
	typedef typename at<tail, i - 1>::type type;
};
template <typename Thead, typename Ttail, typename U> struct at<type_list<Thead, Ttail>, 0, U, false>
{
	typedef Thead type;
};
template <int i, typename U> struct at<null_type, i, U, false>
{
	typedef U type;
};

// get index of some type in type_list
// if T does not exist, value is -1
template <typename Tlist, typename T> struct index
{
private:
	TRAITS_HEAD_TAIL(Tlist);
	enum { temp = index<tail, T>::value };
public:
	enum { value = (temp == -1) ? -1 : 1 + temp };
};
template <typename Ttail, typename T> struct index<type_list<T, Ttail>, T>
{
	enum { value = 0 };
};
template <typename T> struct index<null_type, T>
{
	enum { value = -1 };
};

// check if some type exists in type_list
template <typename Tlist, typename T> struct exists
{
	enum { value = index<Tlist, T>::value >= 0 };
};

// append new type into type_list
template <typename Tlist, typename T> struct append
{
	TRAITS_HEAD_TAIL(Tlist);
	typedef type_list<head, typename append<tail, T>::type> type;
};
template < > struct append<null_type, null_type>
{
	typedef null_type type;
};
template <typename T> struct append<null_type, T>
{
	typedef typename make_type_list<T>::type type;
};
template <typename Thead, typename Ttail> struct append<null_type, type_list<Thead, Ttail> >
{
	typedef type_list<Thead, Ttail> type;
};

// remove a type from type_list
template <typename Tlist, typename T> struct remove
{
	TRAITS_HEAD_TAIL(Tlist);
	typedef type_list<head, typename remove<tail, T>::type> type;
};
template <typename Ttail, typename T> struct remove<type_list<T, Ttail>, T>
{
	typedef Ttail type;
};
template <typename T> struct remove<null_type, T>
{
	typedef null_type type;
};

// remove all apperance of some type
template <typename Tlist, typename T> struct remove_all
{
	TRAITS_HEAD_TAIL(Tlist);
	typedef type_list<head, typename remove_all<tail, T>::type> type;
};
template <typename Ttail, typename T> struct remove_all<type_list<T, Ttail>, T>
{
	typedef typename remove_all<Ttail, T>::type type;
};
template <typename T> struct remove_all<null_type, T>
{
	typedef null_type type;
};

// remove duplicate types in type_list
template <typename Tlist> struct unique
{
private:
	TRAITS_HEAD_TAIL(Tlist);
	typedef typename unique<tail>::type L1;
	typedef typename remove<L1, head>::type L2;
public:
	typedef type_list<head, L2> type;
};
template < > struct unique<null_type>
{
	typedef null_type type;
};

// relpace a type to a new type
template <typename Tlist, typename Told, typename Tnew> struct replace
{
	TRAITS_HEAD_TAIL(Tlist);
	typedef type_list<head, typename replace<tail, Told, Tnew>::type> type;
};
template <typename Ttail, typename Told, typename Tnew> struct replace<type_list<Told, Ttail>, Told, Tnew>
{
	typedef type_list<Tnew, Ttail> type;
};
template <typename Told, typename Tnew> struct replace<null_type, Told, Tnew>
{
	typedef null_type type;
};

// get right types since index-n, n starts from 0.
// eg: TL = <char, short, int, long>, right<TL, 1> = <short, int, long>
// if n < 0 or n >= length<TL>, result type is U. 
template <typename Tlist, int n, typename U = null_type, 
		 int l = (length<Tlist>::value - n), bool is_negetive = (n < 0 || n >= length<Tlist>::value)>
struct right;
template <typename Tlist, int n, typename U, int l> struct right<Tlist, n, U, l, true>
{
	typedef U type;
};
template <typename Tlist, int n, typename U, int l> struct right<Tlist, n, U, l, false>
{
private:
	typedef typename at<Tlist, n>::type head_type;
	typedef typename right<Tlist, n + 1, U, l - 1>::type tail_type;
public:
	typedef type_list<head_type, tail_type> type;
};
template <typename Tlist, int n, typename U> struct right<Tlist, n, U, 0, false>
{
	typedef null_type type;
};

// get right types since type T. 
// eg: TL = <char, short, int, long>, right_t<TL, int> = <int, long>
// if T does not exist, result type is U. 
template <typename TL, typename T, typename U = null_type> struct right_t
{
private:
	enum { idx = index<TL, T>::value };
public:
	typedef typename right<TL, idx, U>::type type;
};

// make type-list by a template type
template <template <typename> class T, typename Tlist> struct make_type_list_by
{
private:
	TRAITS_HEAD_TAIL(Tlist);
	typedef typename make_type_list<T<head> >::type head_type;
	typedef typename make_type_list_by<T, tail>::type tail_type;
public:
	typedef typename append<head_type, tail_type>::type type;
};
template <template <typename> class T> struct make_type_list_by<T, null_type>
{
	typedef null_type type;
};


//////////////////////////
}	// namespace tlop
////////////////
}	// namespace tdl

#endif
